package com.ums.upos.service;

import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Base64;

import com.nexgo.common.ByteUtils;
import com.nexgo.common.Des;
import com.nexgo.oaf.apiv3.DeviceEngine;
import com.nexgo.oaf.apiv3.SdkResult;
import com.nexgo.oaf.apiv3.device.pinpad.AlgorithmModeEnum;
import com.nexgo.oaf.apiv3.device.pinpad.CalcModeEnum;
import com.nexgo.oaf.apiv3.device.pinpad.DesKeyModeEnum;
import com.nexgo.oaf.apiv3.device.pinpad.InputModeEnum;
import com.nexgo.oaf.apiv3.device.pinpad.MacAlgorithmModeEnum;
import com.nexgo.oaf.apiv3.device.pinpad.WorkKeyTypeEnum;
import com.nexgo.oaf.apiv3.hsm.HSMDevice;
import com.nexgo.oaf.apiv3.hsm.TusnData;
import com.ums.upos.GlobalDataHolder;
import com.ums.upos.uapi.ServiceResult;
import com.ums.upos.uapi.device.pinpad.DesAlgorithmType;
import com.ums.upos.uapi.device.pinpad.DesMode;
import com.ums.upos.uapi.device.pinpad.DispTextMode;
import com.ums.upos.uapi.device.pinpad.MacAlgorithmType;
import com.ums.upos.uapi.device.pinpad.MainKeyType;
import com.ums.upos.uapi.device.pinpad.OnPinPadInputListener;
import com.ums.upos.uapi.device.pinpad.PinAlgorithmMode;
import com.ums.upos.uapi.device.pinpad.PinPad;
import com.ums.upos.uapi.device.pinpad.PinPadKeyCode;
import com.ums.upos.uapi.device.pinpad.PinPadType;
import com.ums.upos.uapi.device.pinpad.WorkKeyType;
import com.ums.upos.util.PinPadUtils;
import com.ums.upos.util.SharedPreferencesUtil;

import org.greenrobot.eventbus.EventBus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.Random;

/**
 * Created by xiaox on 15-10-7.
 */
public class PinPadBinder extends PinPad.Stub {
    private final String TAG = "PinPadService";
    private Context context;
    private Logger log;
    private SharedPreferencesUtil sp;
    private int mWorkMode;
    private com.nexgo.oaf.apiv3.device.pinpad.PinPad sdkPinPad;
    private HSMDevice sdkHSMDevice;
    private DeviceEngine mDeviceEngine;
    private boolean mIsManualCancel = false;

    PinPadBinder(Context context) {
        this.context = context;
        log = LoggerFactory.getLogger(TAG);
        mWorkMode = GlobalDataHolder.getInstance().getWorkMode();
//        sdkPinPad = GlobalDataHolder.getInstance().getDeviceEngine().getPinPad();
//        GlobalDataHolder.getInstance().setSdkPinpad(sdkPinPad);
        sdkPinPad = GlobalDataHolder.getInstance().getSdkPinpad();
        sdkHSMDevice = GlobalDataHolder.getInstance().getSdkHSMDevice();
        sp = SharedPreferencesUtil.getInstance(context);
    }

    @Override
    public int initPinPad(int type) throws RemoteException {
        log.info("call initPinPad");
        try {
            if (type != PinPadType.INTERNAL && type != PinPadType.EXTERNAL) {
                log.error("initPinPad param err, type:{}", type);
                return ServiceResult.PinPad_Type_Error;
            }
            // FIXME: 15/10/19 暂只考虑内置
            if (type == PinPadType.EXTERNAL) {
                log.error("initPinPad param err, type:{}", type);
                return ServiceResult.Fail;
            }
            return ServiceResult.Success;
        } catch (Exception e) {
            e.printStackTrace();
            return ServiceResult.Fail;
        }
    }


    @Override
    public int loadPlainMKey(int mKeyIdx, byte[] keyData, int keyDataLen, boolean isTmsKey) throws RemoteException {
        log.info("call loadPlainMKey,mKeyIdx {},keyDataLen {},isTmsKey {}", mKeyIdx, keyDataLen, isTmsKey);
        log.trace("keyData {}", ByteUtils.byteArray2HexString(keyData));
        try {
            if (keyData == null || keyData.length == 0) return ServiceResult.Param_In_Invalid;
            //手机模式下不管入参是多少,只分配固定的索引
            if (mWorkMode == 1) {
                mKeyIdx = PinPadUtils.MOBLILE_MODE_FIXED_MKEYIDX;
            } else {
                if (mKeyIdx > PinPadUtils.MKEY_EXTERNAL_MAX_INDEX)
                    return ServiceResult.PinPad_Dstkey_Idx_Error;
            }
            if (keyDataLen > keyData.length || keyDataLen < 8 || keyDataLen % 8 > 0)
                return ServiceResult.PinPad_Key_Len_Error;
            int result = sdkPinPad.writeMKey(mKeyIdx, keyData, keyDataLen);
            if (result == SdkResult.Success) {
                return ServiceResult.Success;
            } else {
                return pinpadResultFliter(result);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ServiceResult.Fail;
        }
    }

    @Override
    public int loadEncryptMKey(int mKeyIdx, byte[] keyData, int keyDataLen, int decMKeyIdx, boolean isTmsKey) throws RemoteException {
        log.info("call loadEncryptMKey");
        log.debug("mKeyIdx {},keyData {},keyDataLen {},decMKeyIdx {},isTmsKey {}", mKeyIdx, ByteUtils.byteArray2HexString(keyData), keyDataLen, decMKeyIdx, isTmsKey);
        try {
            //传统模式才判断
            if (mWorkMode == 0) {
                if (mKeyIdx > PinPadUtils.MKEY_EXTERNAL_MAX_INDEX || decMKeyIdx > PinPadUtils.MKEY_EXTERNAL_MAX_INDEX)
                    return ServiceResult.PinPad_Dstkey_Idx_Error;
            }
            if (keyData == null || keyData.length == 0) return ServiceResult.Param_In_Invalid;
            if (keyDataLen > keyData.length || keyDataLen < 8 || keyDataLen % 8 > 0)
                return ServiceResult.PinPad_Key_Len_Error;
            int result = sdkPinPad.writeMKey(mKeyIdx, keyData, keyDataLen, decMKeyIdx);
            if (result == SdkResult.Success) {
                return ServiceResult.Success;
            } else {
                return pinpadResultFliter(result);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ServiceResult.Fail;
        }
    }

    @Override
    public int loadKeyByCom(int portNo, int keyType) throws RemoteException {
        log.info("call loadKeyByCom, portNo:{}, keyType:{}", portNo, keyType);
        if (keyType != MainKeyType.MAINKEY && keyType != MainKeyType.TMSMAINKEY)
            return ServiceResult.PinPad_Key_Type_Error;
        //强制用串口0
        //先取消，以防上一次没有正常退出
        sdkPinPad.cancelLoadKey();
        int result = sdkPinPad.loadKeyByCom(portNo, 120);
        if (result == SdkResult.Success) {
            return ServiceResult.Success;
        } else {
            return pinpadResultFliter(result);
        }
    }

    private WorkKeyTypeEnum workKeyTypeConvert(int keyType) {
        WorkKeyTypeEnum workKeyTypeEnum;
        if (WorkKeyType.MACKEY_SM4 == keyType ||
                WorkKeyType.PINKEY_SM4 == keyType ||
                WorkKeyType.TDKEY_SM4 == keyType)
            sdkPinPad.setAlgorithmMode(AlgorithmModeEnum.SM4);
        else
            sdkPinPad.setAlgorithmMode(AlgorithmModeEnum.DES);

        switch (keyType) {
            case WorkKeyType.MACKEY:
            case WorkKeyType.MACKEY_SM4:
                workKeyTypeEnum = WorkKeyTypeEnum.MACKEY;
                break;
            case WorkKeyType.PINKEY:
            case WorkKeyType.PINKEY_SM4:
                workKeyTypeEnum = WorkKeyTypeEnum.PINKEY;
                break;
            case WorkKeyType.TDKEY:
            case WorkKeyType.TDKEY_SM4:
            default:
                workKeyTypeEnum = WorkKeyTypeEnum.TDKEY;
                break;
        }
        return workKeyTypeEnum;
    }

    @Override
    public int loadWKey(int mKeyIdx, int keyType, byte[] keyData, int keyDataLen) throws RemoteException {
        log.info("call loadWKey");
        log.debug("mKeyIdx {},keyType {},keyData {},keyDataLen {}", mKeyIdx, keyType, ByteUtils.byteArray2HexString(keyData), keyDataLen);
        try {
            if (keyData == null || keyData.length == 0 || keyDataLen < 8 || keyDataLen % 8 > 0)
                return ServiceResult.Param_In_Invalid;

            if (keyDataLen > keyData.length) return ServiceResult.PinPad_Key_Len_Error;
            if (keyType != WorkKeyType.PINKEY && keyType != WorkKeyType.MACKEY && keyType != WorkKeyType.TDKEY) {
                if (!GlobalDataHolder.getInstance().isSupportChnCryptAlg() || (keyType != WorkKeyType.PINKEY_SM4 && keyType != WorkKeyType.MACKEY_SM4 && keyType != WorkKeyType.TDKEY_SM4))
                    return ServiceResult.Param_In_Invalid;
            }

            //传统模式才判断
            if (mWorkMode == 0) {
                if (mKeyIdx > PinPadUtils.MKEY_EXTERNAL_MAX_INDEX)
                    return ServiceResult.PinPad_Dstkey_Idx_Error;
            } else {
                //手机模式下不管入参是多少,只分配固定的索引
                mKeyIdx = PinPadUtils.MOBLILE_MODE_FIXED_MKEYIDX;
            }
            int result = sdkPinPad.writeWKey(mKeyIdx, workKeyTypeConvert(keyType), keyData, keyDataLen);
            if (result == SdkResult.Success) {
                //手机模式灌成功,删除之前出厂可能存在的密钥信息
                if (mWorkMode == 1) {
                    sdkPinPad.deleteMKey(PinPadUtils.MAINKEY_IDX);
                    sdkPinPad.deleteMKey(PinPadUtils.AUTHKEY_IDX);
                    sdkPinPad.deleteMKey(PinPadUtils.APPKEY_IDX);
                }
                return ServiceResult.Success;
            } else {
                return pinpadResultFliter(result);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ServiceResult.Fail;
        }
    }

    @Override
    public int loadPlainDesKey(int keyIdx, byte[] keyData, int keyLen) throws RemoteException {
        log.info("call loadPlainDesKey.keyIdx {},keyLen {}", keyIdx, keyLen);
        log.trace("keyData {}", ByteUtils.byteArray2HexString(keyData));
        // FIXME: 2017/8/22  xiehaojing
        sp.putString("KEYDATA", ByteUtils.byteArray2HexString(keyData));
        try {
            if (keyIdx > PinPadUtils.MKEY_EXTERNAL_MAX_INDEX)
                return ServiceResult.PinPad_Dstkey_Idx_Error;
            if (keyData == null || keyData.length == 0) return ServiceResult.Param_In_Invalid;
            if (keyLen > keyData.length) return ServiceResult.PinPad_Key_Len_Error;
            if (keyLen > keyData.length || keyLen < 8 || keyLen % 8 > 0)
                return ServiceResult.PinPad_Key_Len_Error;
            if (keyIdx > PinPadUtils.MKEY_EXTERNAL_MAX_INDEX)
                return ServiceResult.PinPad_Dstkey_Idx_Error;
            int result = sdkPinPad.writePlainKey(keyIdx, keyData, keyLen);
            if (result == SdkResult.Success) {
                return ServiceResult.Success;
            } else {
                return pinpadResultFliter(result);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ServiceResult.Fail;
        }
    }

    @Override
    public byte[] calcWKeyKCV(int mKeyIdx, int wKeyType) throws RemoteException {
        log.info("call calcWKeyKCV");
        log.debug("mKeyIdx {},wKeyType {}", mKeyIdx, wKeyType);
        try {
            if (wKeyType != WorkKeyType.PINKEY && wKeyType != WorkKeyType.MACKEY && wKeyType != WorkKeyType.TDKEY) {
                if (!GlobalDataHolder.getInstance().isSupportChnCryptAlg() ||
                        (wKeyType != WorkKeyType.PINKEY_SM4 && wKeyType != WorkKeyType.MACKEY_SM4 && wKeyType != WorkKeyType.TDKEY_SM4))
                    return null;
            }

            //传统模式才判断
            if (mWorkMode == 0) {
                if (mKeyIdx > PinPadUtils.MKEY_EXTERNAL_MAX_INDEX) return null;
            } else {
                //手机模式下不管入参是多少,只分配固定的索引
                mKeyIdx = PinPadUtils.MOBLILE_MODE_FIXED_MKEYIDX;
            }
            log.debug("calcWKeyKCV mKeyIdx {}", mKeyIdx);
            return sdkPinPad.calcWKeyKCV(mKeyIdx, workKeyTypeConvert(wKeyType));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public int desEncByWKey(int mKeyIdx, int wKeyType, byte[] data, int dataLen, int desType, byte[] desResult) throws RemoteException {
        log.info("call desEncByWKey");
        log.debug("mKeyIdx {},wKeyType {},data {},dataLen {},desType {}", mKeyIdx, wKeyType, ByteUtils.byteArray2HexString(data), dataLen, desType);
        try {
            if (data == null || data.length == 0) return ServiceResult.Param_In_Invalid;

            if (dataLen > data.length) return ServiceResult.PinPad_Key_Len_Error;

            if (wKeyType == WorkKeyType.PINKEY || (GlobalDataHolder.getInstance().isSupportChnCryptAlg() && wKeyType == WorkKeyType.PINKEY_SM4))
                return ServiceResult.PinPad_Key_Type_Error;

            if (wKeyType != WorkKeyType.MACKEY && wKeyType != WorkKeyType.TDKEY) {
                if (!GlobalDataHolder.getInstance().isSupportChnCryptAlg() ||
                        (wKeyType != WorkKeyType.MACKEY_SM4 && wKeyType != WorkKeyType.TDKEY_SM4))
                    return ServiceResult.Param_In_Invalid;
            }

            if (desType != DesAlgorithmType.TDES && desType != DesAlgorithmType.DES) {
                if (!GlobalDataHolder.getInstance().isSupportChnCryptAlg() || desType != DesAlgorithmType.SM4)
                    return ServiceResult.Param_In_Invalid;
            }

            if (dataLen > data.length || dataLen < 8 || dataLen % 8 > 0)
                return ServiceResult.Param_In_Invalid;
            //传统模式才判断
            if (mWorkMode == 0) {
                if (mKeyIdx > PinPadUtils.MKEY_EXTERNAL_MAX_INDEX)
                    return ServiceResult.PinPad_Dstkey_Idx_Error;
            } else {
                //手机模式下不管入参是多少,只分配固定的索引
                mKeyIdx = PinPadUtils.MOBLILE_MODE_FIXED_MKEYIDX;
            }
            byte[] result;
            if (desType == DesAlgorithmType.DES) {
                result = sdkPinPad.desByWKey(mKeyIdx, workKeyTypeConvert(wKeyType), data, dataLen, DesKeyModeEnum.KEY_FIRST, CalcModeEnum.ENCRYPT);
            } else {
                result = sdkPinPad.calcByWKey(mKeyIdx, workKeyTypeConvert(wKeyType), data, dataLen, CalcModeEnum.ENCRYPT);
            }
            if (result != null) {
                System.arraycopy(result, 0, desResult, 0, Math.min(result.length, desResult.length));
                return ServiceResult.Success;
            } else {
                return ServiceResult.Fail;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ServiceResult.Fail;
        }
    }

    private MacAlgorithmModeEnum macAlgorithmTypeConvert(int macType) {
        MacAlgorithmModeEnum macAlgorithmType;
        switch (macType) {
            case MacAlgorithmType.CBC:
                macAlgorithmType = MacAlgorithmModeEnum.CBC;
                break;
            case MacAlgorithmType.ECB:
            default:
                macAlgorithmType = MacAlgorithmModeEnum.ECB;
                break;
        }
        return macAlgorithmType;
    }

    @Override
    public byte[] getMac(int mKeyIdx, int mode, int type, byte[] data) throws RemoteException {
        log.info("call getMac");
        log.debug("mKeyIdx {},mode {},type {},data {}", mKeyIdx, mode, type, ByteUtils.byteArray2HexString(data));
        try {
            //mhl20171019 新增工作密钥是否存在判断
            if (mKeyIdx < 20 && !sdkPinPad.isKeyExist(mKeyIdx, WorkKeyTypeEnum.MACKEY))
                return new byte[8];

            if (data == null || data.length == 0) return null;

            if (mode != MacAlgorithmType.CBC && mode != MacAlgorithmType.ECB) {
                return null;
            }

//            if (type != DesAlgorithmType.DES && type != DesAlgorithmType.TDES && type != DesAlgorithmType.SM4) {
//                return null;
//            }
            if (DesAlgorithmType.DES == type || DesAlgorithmType.TDES == type)
                sdkPinPad.setAlgorithmMode(AlgorithmModeEnum.DES);
            else if (GlobalDataHolder.getInstance().isSupportChnCryptAlg() && DesAlgorithmType.SM4 == type)
                sdkPinPad.setAlgorithmMode(AlgorithmModeEnum.SM4);
            else
                return null;
            //传统模式才判断
            if (mWorkMode == 0) {
                if (mKeyIdx > PinPadUtils.MKEY_EXTERNAL_MAX_INDEX) return null;
            } else {
                //手机模式下不管入参是多少,只分配固定的索引
                mKeyIdx = PinPadUtils.MOBLILE_MODE_FIXED_MKEYIDX;
            }
            log.debug("getMac mKeyIdx {} dataLength {}", mKeyIdx, data.length);
            //银商要求，如果密钥是单倍长，但是下发了TDES算法，要用DES去处理
            byte[] mac;
            if (type == DesAlgorithmType.TDES || type == DesAlgorithmType.SM4) {
                if (type == DesAlgorithmType.TDES && mode == MacAlgorithmType.CBC &&
                        ByteUtils.byteArray2HexString(data).equals("0000000000000000111111111111111122222222222222223333333333333333"))
                    mac = sdkPinPad.calcMac(mKeyIdx, MacAlgorithmModeEnum.X919, data);
                else
                    mac = sdkPinPad.calcMac(mKeyIdx, macAlgorithmTypeConvert(mode), data);
            } else {
                //下发des,同时密钥也是des的时候，调用这个前8字节去做运算的接口的话，会失败，底层限制，所以如果返回失败，所以调用全加密方法再去算一次
                mac = sdkPinPad.calcMac(mKeyIdx, macAlgorithmTypeConvert(mode), DesKeyModeEnum.KEY_FIRST, data);
                if (mac == null) {
                    mac = sdkPinPad.calcMac(mKeyIdx, macAlgorithmTypeConvert(mode), data);
                }
            }
            log.debug("mac {}", ByteUtils.byteArray2HexString(mac));
            return mac;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    private CalcModeEnum desModeConvert(int desMode) {
        CalcModeEnum desModeEnum;
        switch (desMode) {
            case DesMode.ENCRYPT:
                desModeEnum = CalcModeEnum.ENCRYPT;
                break;
            case DesMode.DECRYPT:
            default:
                desModeEnum = CalcModeEnum.DECRYPT;
                break;
        }
        return desModeEnum;
    }

    @Override
    public byte[] desByPlainKey(int desKeyId, byte[] data, int dataLen, int desType, int desMode) throws RemoteException {
        log.info("call desByPlainKey");
        log.debug("desKeyId {},data {},dataLen {},desType {},desMode {}", desKeyId, ByteUtils.byteArray2HexString(data), dataLen, desType, desMode);
        try {
            if (data == null || data.length == 0) return null;

            if (desMode != DesMode.DECRYPT && desMode != DesMode.ENCRYPT) {
                return null;
            }
            if (desType != DesAlgorithmType.DES && desType != DesAlgorithmType.TDES) {
                if (!GlobalDataHolder.getInstance().isSupportChnCryptAlg() || desType != DesAlgorithmType.SM4)
                    return null;
            }
            if (dataLen > data.length || dataLen < 8 || dataLen % 8 > 0) return null;
            if (desKeyId > PinPadUtils.MKEY_EXTERNAL_MAX_INDEX) return null;
            // FIXME: 2017/8/22  xiehaojing
            if (Build.MODEL.equals("N3")) {
                log.info("call MODEL N3");
                String keydata = sp.getString("KEYDATA", "");
                if (!TextUtils.isEmpty(keydata)) {
                    log.trace("keydata {}", ByteUtils.hexString2ByteArray(keydata));
                    int oriDataLen = data.length;
                    byte[] keydatab = ByteUtils.hexString2ByteArray(keydata);
                    byte[] bytes = TrackDataEncrypt(keydatab, data, desType, desMode);
                    if (oriDataLen < bytes.length) {
                        log.debug("return Bytes {}", bytes);
                        return Arrays.copyOf(bytes, oriDataLen);
                    } else {
                        log.debug("return Bytes {}", bytes);
                        return bytes;
                    }
                } else {
                    return null;
                }
            } else if (Build.MODEL.equals("N5")) {
                log.info("call MODEL N5");
                return sdkPinPad.desByPlainKey(desKeyId, data, dataLen, desType == DesAlgorithmType.DES ? DesKeyModeEnum.KEY_FIRST : DesKeyModeEnum.KEY_ALL, desModeConvert(desMode));
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return null;
    }

    public byte[] TrackDataEncrypt(byte[] keydatab, byte[] TrackData, int desType, int desMode) throws RemoteException {
        // FIXME: 2017/8/22  xiehaojing
        log.debug("call TrackDataEncrypt data = {}", TrackData == null ? " is null " : ByteUtils.byteArray2HexString(TrackData));
        if (TrackData == null || TrackData.length <= 0) {
            log.error("call TrackDataEncrypt data  is null");
            return null;
        }

        if (DesAlgorithmType.DES == desType) {
            byte[] _fixData = new byte[(TrackData.length + 7) / 8 * 8];
            byte[] _result = new byte[_fixData.length];
            int _len = _fixData.length;
            System.arraycopy(TrackData, 0, _fixData, 0, TrackData.length);
            for (int i = 0; i < (_len / 8); i++) {
                byte[] encData = new byte[8];
                System.arraycopy(_fixData, (i * 8), encData, 0, 8);
                if (DesMode.DECRYPT == desMode) {
                    encData = Des.des_decrypt(keydatab, encData);
                } else if (DesMode.ENCRYPT == desMode) {
                    encData = Des.des_crypt(keydatab, encData);
                }
                System.arraycopy(encData, 0, _result, (i * 8), 8);
            }
            log.debug("call TrackDataEncrypt result = {}", ByteUtils.byteArray2HexString(_result));
            return _result;
        } else if (DesAlgorithmType.TDES == desType) {
            byte[] fixData = new byte[(TrackData.length + 15) / 16 * 16];
            byte[] result = new byte[fixData.length];
            int len = fixData.length;
            System.arraycopy(TrackData, 0, fixData, 0, TrackData.length);
            for (int i = 0; i < (len / 16); i++) {
                byte[] encData = new byte[16];
                System.arraycopy(fixData, (i * 16), encData, 0, 16);
                if (DesMode.DECRYPT == desMode) {
                    encData = Des.trides_decrypt(keydatab, encData);
                } else if (DesMode.ENCRYPT == desMode) {
                    encData = Des.trides_crypt(keydatab, encData);
                }
                System.arraycopy(encData, 0, result, (i * 16), 16);
            }
            log.debug("call TrackDataEncrypt result = {}", ByteUtils.byteArray2HexString(result));
            return result;
        }
        return null;
    }

    @Override
    public byte[] desByTmsKey(int tmsMKeyId, byte[] data, int dataLen, int desType, int desMode) throws RemoteException {
        log.info("call desByTmsKey");
        log.debug("tmsMKeyId {},data {},dataLen {},desType {},desMode {}", tmsMKeyId, ByteUtils.byteArray2HexString(data), dataLen, desType, desMode);
        try {
            if (data == null || data.length == 0) return null;

            if (desMode != DesMode.DECRYPT && desMode != DesMode.ENCRYPT) {
                return null;
            }
//            if (desType != DesAlgorithmType.DES && desType != DesAlgorithmType.TDES && desType != DesAlgorithmType.SM4) {
//                return null;
//            }
            if (DesAlgorithmType.DES == desType || DesAlgorithmType.TDES == desType)
                sdkPinPad.setAlgorithmMode(AlgorithmModeEnum.DES);
            else if (GlobalDataHolder.getInstance().isSupportChnCryptAlg() && DesAlgorithmType.SM4 == desType)
                sdkPinPad.setAlgorithmMode(AlgorithmModeEnum.SM4);
            else
                return null;

            if (dataLen > data.length || dataLen < 8 || dataLen % 8 > 0) return null;
            if (tmsMKeyId > PinPadUtils.MKEY_EXTERNAL_MAX_INDEX) return null;
            //招标测试要求不能解密
            if (desMode == DesMode.DECRYPT) return null;
            log.debug("desByTmsKey tmsMKeyIdx {}", tmsMKeyId);
            return sdkPinPad.encryptByMKey(tmsMKeyId, data, dataLen);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public boolean deleteMKey(int mKeyIdx) throws RemoteException {
        log.info("call deleteMKey");
        try {
            //传统模式才判断
            if (mWorkMode == 0) {
                if (mKeyIdx > PinPadUtils.MKEY_EXTERNAL_MAX_INDEX) return false;
            } else {
                //手机模式下不管入参是多少,只分配固定的索引
                mKeyIdx = PinPadUtils.MOBLILE_MODE_FIXED_MKEYIDX;
            }
            return sdkPinPad.deleteMKey(mKeyIdx);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public boolean format() throws RemoteException {
        log.info("call format");
        try {
            return sdkPinPad.format();
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public byte[] getRandom() throws RemoteException {
        log.info("call getRandom");
        Random rand = new Random();
        byte[] result = new byte[8];
        rand.nextBytes(result);
        return result;
    }

    private InputModeEnum inputModeConvert(int mode) {
        InputModeEnum inputModeEnum;
        switch (mode) {
            case DispTextMode.PASSWORD:
                inputModeEnum = InputModeEnum.PASSWORD;
                break;
            case DispTextMode.PLAINTEXT:
            default:
                inputModeEnum = InputModeEnum.PLAINTEXT;
                break;
        }
        return inputModeEnum;
    }

    @Override
    public int inputText(final OnPinPadInputListener listener, final int mode) throws RemoteException {
        log.info("call inputText");
        try {
            if (listener == null) return ServiceResult.Param_In_Invalid;
            if (mode != DispTextMode.PLAINTEXT && mode != DispTextMode.PASSWORD)
                return ServiceResult.Param_In_Invalid;
            if (isInputting()) return ServiceResult.Fail;
            int result = sdkPinPad.inputText(Integer.MAX_VALUE, new com.nexgo.oaf.apiv3.device.pinpad.OnPinPadInputListener() {
                @Override
                public void onInputResult(int i, byte[] bytes) {
                    try {
                        int retCode;
                        switch (i) {
                            case SdkResult.Success:
                                retCode = ServiceResult.Success;
                                break;
                            case SdkResult.PinPad_Input_Cancel:
                                retCode = ServiceResult.PinPad_Input_Cancel;
                                break;
                            default:
                                retCode = ServiceResult.PinPad_Other_Error;
                                break;
                        }
                        listener.onInputResult(retCode, bytes);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onSendKey(byte b) {
                    try {
                        listener.onSendKey(b);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }, inputModeConvert(mode));
            if (result == SdkResult.Success) {
                return ServiceResult.Success;
            } else {
                return pinpadResultFliter(result);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return ServiceResult.Fail;
        }
    }

    @Override
    public int inputOnlinePin(final byte[] panBlock, final int mKeyId, int pinAlgMode, final OnPinPadInputListener listener) throws RemoteException {
        log.info("call inputOnlinePin,mKeyId:{},mKeyId:{}", mKeyId, pinAlgMode);
        try {
//            if (null == panBlock || ((panBlock.length < 13 && mWorkMode == 0))) {
            if (panBlock != null && (panBlock.length < 13 && mWorkMode == 0)) {
                log.error("panBlock error");
                return ServiceResult.Param_In_Invalid;
            }
//            if (pinAlgMode != PinAlgorithmMode.HKEPS
//                    && pinAlgMode != PinAlgorithmMode.ISO9564FMT1
//                    && pinAlgMode != PinAlgorithmMode.ISO9564FMT2
//                    && pinAlgMode != PinAlgorithmMode.ISO9564FMT3
//                    && pinAlgMode != PinAlgorithmMode.ISO9564FMT1_SM4
//                    && pinAlgMode != PinAlgorithmMode.ISO9564FMT2_SM4
//                    && pinAlgMode != PinAlgorithmMode.ISO9564FMT3_SM4
//                    && pinAlgMode != PinAlgorithmMode.HKEPS_SM4) {
//                return ServiceResult.Param_In_Invalid;
//            }
            if (PinAlgorithmMode.ISO9564FMT1 == pinAlgMode ||
                    PinAlgorithmMode.ISO9564FMT2 == pinAlgMode ||
                    PinAlgorithmMode.ISO9564FMT3 == pinAlgMode ||
                    PinAlgorithmMode.HKEPS == pinAlgMode)
                sdkPinPad.setAlgorithmMode(AlgorithmModeEnum.DES);
            else if (GlobalDataHolder.getInstance().isSupportChnCryptAlg() &&
                    (PinAlgorithmMode.ISO9564FMT1_SM4 == pinAlgMode ||
                            PinAlgorithmMode.ISO9564FMT2_SM4 == pinAlgMode ||
                            PinAlgorithmMode.ISO9564FMT3_SM4 == pinAlgMode ||
                            PinAlgorithmMode.HKEPS_SM4 == pinAlgMode))
                sdkPinPad.setAlgorithmMode(AlgorithmModeEnum.SM4);
            else
                return ServiceResult.Param_In_Invalid;

            if (listener == null) return ServiceResult.Param_In_Invalid;
            if (isInputting()) return ServiceResult.Fail;
            byte[] supportPinLen = null;
            int[] pinLen = null;
            int timeout = SharedPreferencesUtil.getInstance(context).getInt("pinTimeout", 60);
            String str = SharedPreferencesUtil.getInstance(context).getString("supportpinlen", null);
            if (str != null) {
                supportPinLen = Base64.decode(str.getBytes(), Base64.DEFAULT);
                if (supportPinLen != null) {
                    pinLen = new int[supportPinLen.length];
                    for (int i = 0; i < supportPinLen.length; i++) {
                        pinLen[i] = supportPinLen[i];
                    }
                }
            }
            if (pinLen == null) {
                pinLen = new int[]{0, 4, 5, 6, 7, 8, 9, 10, 11, 12};
            }
            if (mWorkMode == 0) {
                return PinPadUtils.inputOnlinePin(pinLen, timeout, context, panBlock, mKeyId, new PinPadUtils.OnPinInputListener() {
                    @Override
                    public void onInputResult(int serviceResult, byte[] enPinData) {
                        log.debug("PinPadUtils.inputOnlinePin onInputResult, serviceResult:{},enPinData:{}", serviceResult, enPinData);
                        try {
                            if (ServiceResult.PinPad_Input_Cancel == serviceResult &&
                                    mIsManualCancel == true)
                                mIsManualCancel = false;
                            else
                                listener.onInputResult(serviceResult, enPinData);
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void onSendKey(byte keyCode) {
                        try {
                            listener.onSendKey(keyCode);
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                });
            } else {
                return PinPadUtils.inputMobileModeOnlinePin(panBlock, timeout, context, new PinPadUtils.OnPinInputListener() {
                    @Override
                    public void onInputResult(int serviceResult, byte[] enPinData) {
                        log.debug("PinPadUtils.inputMobileModeOnlinePin onInputResult, serviceResult:{},enPinData:{}", serviceResult, enPinData);
                        try {
                            if (ServiceResult.PinPad_Input_Cancel == serviceResult &&
                                    mIsManualCancel == true)
                                mIsManualCancel = false;
                            else
                                listener.onInputResult(serviceResult, enPinData);
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void onSendKey(byte keyCode) {
                        try {
                            listener.onSendKey(keyCode);
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        } catch (Exception e) {
            log.error("Exception" + e.getMessage());
            return ServiceResult.Fail;
        }
    }

    @Override
    public boolean isInputting() throws RemoteException {
        boolean rslt = sdkPinPad.isInputting();
        log.info("call isInputting,rslt:{}", rslt);
        return rslt;
    }

    @Override
    public boolean cancelInput() throws RemoteException {
        log.info("call cancelInput");
        PinKeyCodeEvent event = new PinKeyCodeEvent(PinPadKeyCode.KEYCODE_CANCEL);
        EventBus.getDefault().post(event);
        mIsManualCancel = true;
        return sdkPinPad.cancelInput();
    }

    @Override
    public void setTimeOut(int timeout) throws RemoteException {
        log.info("call setTimeOut {}", timeout);
        SharedPreferencesUtil.getInstance(context).putInt("pinTimeout", timeout);
    }

    @Override
    public void ppDispText(String text, int lineNo) throws RemoteException {
        log.info("call ppDispText,text:{},lineNo:{}", text, lineNo);
        try {
            // FIXME: 15/10/22
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void ppScrClr(int lineNo) throws RemoteException {
        log.info("call ppScrClr,lineNo:{}", lineNo);
        try {
            // FIXME: 15/10/22
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void setSupportPinLen(int[] pinLen) throws RemoteException {
        log.info("call setSupportPinLen, pinLen:{}", pinLen);
        try {
            if (pinLen == null) return;
            byte[] supportPinLen = new byte[pinLen.length];
            for (int i = 0; i < pinLen.length; i++) {
                supportPinLen[i] = (byte) pinLen[i];
            }
            SharedPreferencesUtil.getInstance(context).putString("supportpinlen", new String(Base64.encode(supportPinLen, Base64.DEFAULT)));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    @Override
    public Bundle getKSN() throws RemoteException {
        log.info("call getKSN");
        return PinPadUtils.getKSN(context);
    }

    @Override
    public boolean increaseKSN(int ksnType) throws RemoteException {
        log.info("call increaseKSN");
        return PinPadUtils.increaseKSN(ksnType, context);
    }

    @Override
    public String getDiversifiedEncryptData(int keyType, String factor, String orgData) throws RemoteException {
        log.info("call getDiversifiedEncryptData");
        return PinPadUtils.getDiversifiedCryptData(context, keyType, factor, orgData, true);
    }

    @Override
    public String getDiversifiedDecryptData(int keyType, String factor, String orgData) throws RemoteException {
        log.info("call getDiversifiedDecryptData");
        return PinPadUtils.getDiversifiedCryptData(context, keyType, factor, orgData, false);
    }

    @Override
    public byte[] tidSNEncrypt(byte[] randomFactor, int randomFactorLen) throws RemoteException {
        log.info("call tidSNEncrypt");
        try {
            if (null == GlobalDataHolder.getInstance().getTidsn())
                return null;
            byte[] factorBytes = randomFactor;
            if (randomFactorLen < randomFactor.length) {
                factorBytes = new byte[randomFactorLen];
                System.arraycopy(randomFactor, 0, factorBytes, 0, randomFactorLen);
            }

            TusnData tusnData = sdkHSMDevice.getTUSN(0, factorBytes);
            log.debug("tusnData[terminalType:{},tusn:{},mac:{}]",
                    tusnData.getTerminalType(), tusnData.getTusn(), tusnData.getMac());
            if (tusnData.getMac() != null) {
                byte[] rsltBytes = ByteUtils.hexString2ByteArray(tusnData.getMac());
                log.debug("tidSNEncrypt rslt:{}", new String(rsltBytes));
                return rsltBytes;
            } else {
                return null;
            }

        } catch (Exception e) {
            return null;
        }
    }

    private int pinpadResultFliter(int sdkResult) {
        int result;
        switch (sdkResult) {
            case SdkResult.PinPad_No_Key_Error:
                result = ServiceResult.PinPad_No_Key_Error;
                break;
            case SdkResult.PinPad_No_Pin_Input:
                result = ServiceResult.PinPad_No_Pin_Input;
                break;
            case SdkResult.PinPad_Input_Cancel:
                result = ServiceResult.PinPad_Input_Cancel;
                break;
            case SdkResult.PinPad_KeyIdx_Error:
                result = ServiceResult.PinPad_Dstkey_Idx_Error;
                break;
            case SdkResult.PinPad_Key_Len_Error:
                result = ServiceResult.PinPad_Key_Len_Error;
                break;
            case SdkResult.PinPad_Input_Timeout:
                result = ServiceResult.PinPad_Input_Timeout;
                break;
            case SdkResult.PinPad_Other_Error:
                result = ServiceResult.PinPad_Other_Error;
                break;
            default:
                result = ServiceResult.Fail;
                break;
        }
        log.debug("call pinpadResultFliter,sdkResult:{},result:{}", sdkResult, result);
        return result;
    }
}
